#ifndef LIST_H
#define LIST_H

#include "scrollbar.h"
#include "../../image_utils.h"

#include <QStringList>
#include <QMutex>

class List;

class ListListener
{
public:
    virtual void OnSelect(List* sender, int index) = 0;
    virtual void OnDoubleClick(List* sender, int index) = 0;
};

class List: public Widget, public ScrollBarListener
{
public:
    enum ScrollBarPosition {Left, Right};

    List(Widget* _parent, Engine* _engine, int _x, int _y, int _w, int _h, int scrollwidth,
         ScrollBarPosition _scrollPosition = Right)
             :Widget(_parent, _engine, _x, _y, _w, _h),
             pixmap(10, 10),
             selectionLayer(10, 10),
             font("Palatino", 12),
             pixmapPoint(0, 0),
             transparentBrush( QColor(255, 255, 255, 100), Qt::SolidPattern ),
             blackPen(QColor(0, 0, 0, 255)),
             pressPoint(-1, -1),
             selectionBrush(QColor(255, 128, 0, 50))
    {
        listChanged = false;
        firstClick = false;
        scrollbarWidth = scrollwidth;
        hover_index = -1;
        scrollPosition = _scrollPosition;

        if(scrollPosition == Right)
        {
            listRect = QRect(0, 0, w - scrollwidth, h - scrollwidth);

            verticalScroll = new ScrollBar(this, engine, w - scrollwidth, 0, scrollwidth, h - scrollwidth,
                                           ScrollBar::Vertical, h - scrollwidth, 10);
            horizontalScroll = new ScrollBar(this, engine, 0, h - scrollwidth, w - scrollwidth, scrollwidth,
                                             ScrollBar::Horizontal, w - scrollwidth, 10);
        }else
        {
            listRect = QRect(scrollwidth, 0, w - scrollwidth, h - scrollwidth);
            verticalScroll = new ScrollBar(this, engine, 0, 0, scrollwidth, h - scrollwidth,
                                           ScrollBar::Vertical, h - scrollwidth, 10);
            horizontalScroll = new ScrollBar(this, engine, scrollwidth, h - scrollwidth, w - scrollwidth,
                                             scrollwidth, ScrollBar::Horizontal, w - scrollwidth, 10);
        }

        verticalScroll->AddListener(this);
        AddChild(verticalScroll);
        horizontalScroll->AddListener(this);
        AddChild(horizontalScroll);

        SetFont(font);
        selected = -1;

        mouseTracking = true;

        time.start();
        uglyTime.start();
    }

    virtual ~List(){}

    void SetFont(const QFont& _font)
    {
        font = _font;
        fontHeight = ImageUtils::GetTextHeight("S", font);
        lineHeight = fontHeight + 16;
        UpdatePixmap();
    }

    void Add(const QString& str)
    {
        sList.append(str);
        listChanged = true;
        UpdatePixmap();
    }

    virtual void Paint(QPainter &painter)
    {
        painter.setBrush(transparentBrush);
        painter.setPen(blackPen);

        int hover_x = x;
        if(scrollPosition == Right)
        {
            painter.drawRect(x, y, w - scrollbarWidth, h - scrollbarWidth);
            painter.drawPixmap(x, y, pixmap, pixmapPoint.x(), pixmapPoint.y(), w - scrollbarWidth, h - scrollbarWidth);
            painter.drawPixmap(x, y, selectionLayer, pixmapPoint.x(), pixmapPoint.y(), w - scrollbarWidth, h - scrollbarWidth);
        }else
        {
            hover_x += scrollbarWidth;
            painter.drawRect(x + scrollbarWidth, y, w - scrollbarWidth, h - scrollbarWidth);
            painter.drawPixmap(x + scrollbarWidth, y, pixmap, pixmapPoint.x(), pixmapPoint.y(), w - scrollbarWidth, h - scrollbarWidth);
            painter.drawPixmap(x + scrollbarWidth, y, selectionLayer, pixmapPoint.x(), pixmapPoint.y(), w - scrollbarWidth, h - scrollbarWidth);
        }

        if( hover_index != -1 && hover_index < sList.size() )
        {
            int hover_y = y - (pixmapPoint.y() % lineHeight) + hover_index * lineHeight ;
            if( hover_y >= y && hover_y + lineHeight < y + h-scrollbarWidth )
            {
                painter.setPen( QColor( 0, 0, 255, 150 ) );
                painter.setBrush( QBrush(QColor( 0, 0, 255, 50 ), Qt::SolidPattern) );
                painter.drawRect( hover_x, hover_y, w - scrollbarWidth, lineHeight );
            }
        }
    }

    virtual void SetStringList(const QStringList& _sList)
    {
        sList = QStringList(_sList);
        listChanged = true;
        selected = -1;
        UpdatePixmap();
    }

    virtual void OnMousePressed(QMouseEvent *e, bool inside, int x_rel, int y_rel)
    {
        if(listRect.contains(x_rel, y_rel, true))
        {
            if(e->button() == Qt::RightButton)
            {   // list dragging
                // enables horizontal scroll if necessary
                if(pixmap.width() > w)
                {
                    pressPoint.setX(x_rel);
                }

                // enables vertical scroll if necessary
                if(pixmap.height() > h)
                {
                    pressPoint.setY(y_rel);
                }

                oldPixmapPoint = pixmapPoint;
            }else if(e->button() == Qt::LeftButton)
            {   // list selection
                if(time.elapsed() > 200)
                {
                    UpdateSelection(x_rel, y_rel);
                    time.restart();
                }else
                {
                    if(selected >= 0 && selected < sList.size())
                    {
                        for(int i=0; i<listeners.size(); i++)
                        {
                            listeners.at(i)->OnDoubleClick(this, selected);
                        }
                    }
                }
            }
        }
    }

    virtual void OnMouseReleased(QMouseEvent *e, bool inside, int x_rel, int y_rel)
    {
        pressPoint.setX(-1);
        pressPoint.setY(-1);
    }

    virtual void OnMouseMoved(QMouseEvent *e, bool onWidget, int x_rel, int y_rel)
    {
        if(!visible) return;

        if(pressPoint.y() != -1)
        {
            int newPixmapPointY = oldPixmapPoint.y() + pressPoint.y() - y_rel;
            if(newPixmapPointY + h - scrollbarWidth < pixmap.height() && newPixmapPointY > 0)
            {
                pixmapPoint.setY(newPixmapPointY);
            }
        }

        if(pressPoint.x() != -1)
        {
            int newPixmapPointX = oldPixmapPoint.x() + pressPoint.x() - x_rel;
            if(newPixmapPointX > 0 && newPixmapPointX + w - scrollbarWidth < pixmap.width())
            {
                pixmapPoint.setX(newPixmapPointX);
            }
        }

        if( onWidget )
        {
            hover_index = (y_rel/(float)lineHeight);
        }
        else
        {
            hover_index = -1;
        }
    }

    virtual int GetSelectedIndex()
    {
        if( sList.size() == 0 ) return -1;
        return selected;
    }

    virtual QString GetSelectedString()
    {
        if(selected != -1)
        {
            return sList.at(selected);
        }
        return "";
    }

    // ScrollBarListener implementation
    virtual void OnPositionChanged(ScrollBar* scrollbar, int newPos)
    {
        if(scrollbar == verticalScroll)
        {
            pixmapPoint.setY(newPos);
        }else
        {
            pixmapPoint.setX(newPos);
        }
    }

    // listeners handling
    void AddListListener(ListListener* ll)
    {
        listeners.append(ll);
    }

    void SetItemSelected( int selectIndex, bool notify = true )
    {
        if( selectIndex >= 0 && selectIndex<sList.size() )
        {
            selected = selectIndex;
            UpdateSelection( -1, -1, false, notify );
        }
    }

    void SetItemAt( int index, const QString& newText, bool notifyListeners = true )
    {
        if( index >= 0 && index < sList.size() )
        {
            sList.removeAt( index );
            sList.insert( index, newText );
            UpdatePixmap();
            UpdateSelection( -1, -1, false, notifyListeners );
        }
    }

protected:
    QPixmap pixmap;
    QPixmap selectionLayer;
    QFont font;
    QStringList sList;
    bool listChanged;
    QPoint pixmapPoint;

    int lineHeight;
    int fontHeight;

    QBrush transparentBrush;
    QPen blackPen;

    QPoint pressPoint;
    QPoint oldPixmapPoint;

    int selected;
    int hover_index;
    QBrush selectionBrush;

    int scrollbarWidth;
    ScrollBar* verticalScroll;
    ScrollBar* horizontalScroll;

    QRect listRect;

    ScrollBarPosition scrollPosition;

    QList< ListListener* > listeners;

    QTime time;
    bool firstClick;

    void UpdatePixmap()
    {
        if(listChanged)
        {
            AdjustPixmapSizeIfNecessary(pixmap);
            listChanged = false;
        }

        pixmap.fill(QColor(0, 0, 0, 0));
        selectionLayer.fill(QColor(0, 0, 0, 0));
        QPainter p(&pixmap);
        p.setPen(blackPen);
        p.setFont(font);

        int textY = lineHeight - 6;
        for(int i=0; i<sList.size(); i++)
        {
            p.drawText(5, lineHeight * i + textY, sList.at(i));
        }
    }

    void AdjustPixmapSizeIfNecessary(QPixmap& targetPixmap)
    {
        int maxHeight = sList.size() * lineHeight + lineHeight;
        bool adjustHeight = (maxHeight > targetPixmap.height());

        int maxWidth = 0;
        for(int i=0; i<sList.size(); i++)
        {
            maxWidth = qMax<int>(maxWidth, ImageUtils::GetTextWidth(sList.at(i), font));
        }
        maxWidth += 30;
        maxWidth = qMax<int>(maxWidth, w);
        bool adjustWidth = (maxWidth > targetPixmap.width());

        if(adjustWidth || adjustHeight)
        {
            targetPixmap = targetPixmap.scaled(adjustWidth? maxWidth : targetPixmap.width(),
                                               adjustHeight? maxHeight : targetPixmap.height());
            selectionLayer = selectionLayer.scaled(targetPixmap.width(), targetPixmap.height());
        }

        verticalScroll->SetWindowSize(maxHeight);
        horizontalScroll->SetWindowSize(targetPixmap.width());
    }

    void UpdateSelection(int x_rel, int y_rel, bool calcSelection = true, bool notifyListeners = true )
    {
        selectionLayer.fill(QColor(0, 0, 0, 0));
        QPainter selectionPainter(&selectionLayer);

        int tmp = selected;
        if( calcSelection )
        {
            tmp = ((pixmapPoint.y() + y_rel)/(float)lineHeight);
        }

        if(tmp >= 0 && tmp < sList.size())
        {
            selected = tmp;
            selectionPainter.setBrush(selectionBrush);
            selectionPainter.setPen(QColor(0, 0, 0, 0));
            selectionPainter.drawRect(0, selected * lineHeight, selectionLayer.width(), lineHeight);

            if( notifyListeners )
            {
                for(int i=0; i<listeners.size(); i++)
                {
                    listeners.at(i)->OnSelect(this, selected);
                }
            }
        }
    }

    QTime uglyTime;
};

#endif // LIST_H
